package com.gta.edu.sdk.mybatis.common;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.RowSet;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import com.gta.edu.sdk.common.cache.NdsCachedRowSetImpl;
import com.gta.edu.sdk.util.EntityTableReflectUtil;
import com.gta.edu.sdk.util.IdGen;
import com.gta.edu.sdk.util.StringUtil;

/**
 * 数据访问层的基类，封装一些通用的方法
 * @author ya.feng
 *
 * @param <T>
 */
public class BaseDao<T> extends SqlSessionDaoSupport{
	private static final Logger LOG = LogManager.getLogger(BaseDao.class);
	
	/**
	 * 注入回话工厂
	 */
	@Resource
	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){
		super.setSqlSessionFactory(sqlSessionFactory);
	}
	
	/**
	 * 根据key得到对应的sql语句进行对象(任何实体类型)保存操作
	 * @param key
	 * @param entity
	 * @return
	 */
	public int save(String key, Entity entity) {
		if (StringUtil.isNullOrEmpty(entity.getId())) {
			entity.setId(IdGen.uuid());
		}
		return getSqlSession().insert(key, entity);
	}
	
	/**
	 * 根据实体对象更新对应的记录数据，实体类对象中，必须要有对应的ID的值
	 * @param key
	 * @param entity
	 * @return
	 */
	public int update(String key, Entity entity) {
		return getSqlSession().update(key, entity);
	}
	
	/**
	 * 根据参数执行更新操作
	 * @param key
	 * @param param
	 * @return
	 */
	public int update(String key, Map<String, Object> param) {
		return getSqlSession().update(key, param);
	}
	
	/**
	 * 对实体类对象进行添加、更新操作，依靠对象的ID的值，如果ID为空，则对数据进行添加操作，否则对数据进行更新操作
	 * @param key
	 * @param entity
	 * @return
	 */
	public int saveOrUpdate(String key, Entity entity) {
		if (StringUtil.isNullOrEmpty(entity.getId())) {
			return this.save(key, entity);
		} else {
			return this.update(key, entity);
		}
	}
	
	/**
	 * 根据id进行删除记录数据
	 * @param key
	 * @param id
	 * @return
	 */
	public int delete(String key, Map<String,Object> params) {
		return getSqlSession().delete(key, params);
	}
	
	/**
	 * 根据查询sql语句和参数，得到一条指定类型的实体类的对象
	 * @param sql
	 * @param cls
	 * @param params
	 * @return
	 */
	public T get(String sql,Class<T> cls, Object...params){
		try {
			RowSet rs = this.queryset(sql, params);
			if(rs.next()){
				return EntityTableReflectUtil.getObject(cls, rs);
			}
		} catch (Exception e) {
			LOG.error("查询出现了异常",e);
		}
		return null;
	}
	
	/**
	 * 执行sql语句
	 * 
	 * @param sql
	 * @param params
	 *            执行sql时的参数
	 * @throws DaoException
	 */
	public RowSet queryset(String sql, Object...params) {
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		com.sun.rowset.CachedRowSetImpl crs = null;
		try {
			conn = getConnection();
			LOG.info(sql);
			stmt = conn.prepareStatement(sql);
			fillParams(stmt, params);
			rs = stmt.executeQuery();
//			crs = new com.sun.rowset.CachedRowSetImpl();
			crs = new NdsCachedRowSetImpl();
			crs.populate(rs);
		} catch (Exception e) {
			//e.printStackTrace();
			LOG.error(sql, e);
		} finally {
			releaseConnection(rs, stmt, conn);
		}
		return crs;
	}
	
	/**
	 * @description 得到数据库连接
	 * @author fengya
	 * @date 2017年6月30日 下午2:54:43
	 * @return
	 * @return Connection
	 */
	private Connection getConnection() {
		try {
			Connection conn = getSqlSession().getConfiguration().getEnvironment().getDataSource().getConnection();
			conn.setAutoCommit(false);
			return conn;
		} catch (SQLException e) {
			LOG.error("数据库连接异常：", e);
			return null;
		}

	}
	
	/**
	 * @description 释放连接
	 * @author fengya
	 * @date 2017年6月30日 下午2:54:56
	 * @param rs
	 * @param stmt
	 * @param connection
	 * @return void
	 */
	private void releaseConnection(ResultSet rs, Statement stmt, Connection connection) {
		try {
			if (rs != null) {
				rs.close();
			}
			if (stmt != null) {
				stmt.close();
			}
			if (connection != null) {
				connection.close();
			}
		} catch (Exception e) {
			LOG.error(getClass().getName(), e);
		}
	}
	
	/**
	 * @description sql参数赋值
	 * @author fengya
	 * @date 2017年6月30日 下午2:55:17
	 * @param ps
	 * @param params
	 * @return void
	 */
	private void fillParams(PreparedStatement ps, Object[] params) {
		if (params != null && params.length != 0) {
			for (int i = 0; i < params.length; i++) {
				try {
					if (params[i] == null) {
						ps.setNull(i + 1, Types.VARCHAR);
					} else {
						ps.setObject(i + 1, params[i]);
					}
				} catch (Exception e) {
					LOG.error(getClass().getName(), e);
				}
			}
		}
	}
	
	/**
	 * @description 查询只有一个对象的语句
	 * @author fengya
	 * @date 2017年7月3日 上午9:55:45
	 * @param key
	 * @param params
	 * @return
	 * @return Object
	 */
	public Object getObject(String key, Object params) {
		try {
			return getSqlSession().selectOne(key, params);
		} catch (Exception e) {
			LOG.error(getClass().getName() + " get exception and key is" + key, e);
		}
		return null;
	}
	
	/**
	 * @description 多记录查询
	 * @author fengya
	 * @date 2017年7月3日 上午9:57:06
	 * @param key
	 * @param params
	 * @return
	 * @return List<T>
	 */
	public List<T> getList(String key, Object params) {
		try {
			return getSqlSession().selectList(key, params);
		} catch (Exception e) {
			LOG.error(getClass().getName() + " getList exception and key is" + key, e);
		}
		return null;
	}
	
	/**
	 * 数据库批量处理
	 * 
	 * @param sql
	 * @param params
	 * @return
	 * @throws SQLException
	 */
	public int[] executeBatch(String sql, Object[][] params) throws SQLException {
		Connection connection = null;
		PreparedStatement ps = null;
		try {
			connection = getConnection();
			ps = connection.prepareStatement(sql);
			if(params != null){
				for (int i = 0; i < params.length; i++) {
					fillParams(ps, params[i]);
					ps.addBatch();
				}
			}
			return ps.executeBatch();
		} catch (Exception e) {
			LOG.error(getClass().getName(), e);
			throw new SQLException("batch error", e);
		} finally {
			try {
				if (connection != null) {
					connection.commit();
					connection.setAutoCommit(false);
				}
			} catch (Exception e) {
				LOG.error(getClass().getName(), e);
				throw new SQLException("batch error", e);
			}
			releaseConnection(null, ps, connection);
		}
	}
}
